import {convertLogTimeStringToStamp} from "./time-format.js";

function convertRtStreamToStreamId(rec, context, fields) {
    for (let field of fields) {
        let rtStreamAddr = rec[field];
        if (!context.rtStreamAddressesToStreamId.has(rtStreamAddr)) {
            return null;
        }
        rec[field] = context.rtStreamAddressesToStreamId.get(rtStreamAddr);
    }
    return rec;
}

export const RecordCategory = {
    Memory: "memory",
    Pool: "pool",
    Event: "event"
};

/**
 * 一条日志的基础格式为：
 * [INFO] GE(1600298,python3):2023-01-01-10:40:20.220.150 [file_name.cc:48] 1600298 Log Content
 * 在log content前的正则表达式可以写成：
 * .*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+)\s+\[.*\]\s+\d+\s+
 * 简单一些，也可以这样写：
 * .*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*
 */
export class RecordType {
    static all = []

    static linkStream = RecordType.#addRecordType('link stream',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]Get rts stream (?<stream>((\(nil\))|(0x\w+))) from logical stream (?<streamId>\d+)/)

    // [MEM]Alloc memory at stream 0, block 0x12424324a, address 0x12341as233, size 655536
    static allocMem = RecordType.#addMemory('alloc',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Alloc memory at stream (?<stream>-?\d+), block (?<blockAddress>((\(nil\))|(0x\w+))), address (?<address>((\(nil\))|(0x\w+))), size (?<size>\d+)/)

    // 释放内存的动作，有这条动作可能是引用计数减，也可能是流上回收，可能是真正的释放（单流使用时）
    // [MEM]Free memory at stream 0, address 0x12341as233
    // todo 还没改完
    static freeMem = RecordType.#addMemory('free',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Free memory at stream (?<stream>-?\d+), address (?<address>0x\w+)/)

    // AccessMemCrossStream kernel打出的日志
    // [MEM]Memory wandering from stream 0 to 1, address 0x12341234
    static wander = RecordType.#addMemory('wandering',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Memory wandering from stream (?<srcStream>-?\d+) to (?<stream>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Local-recycle memory at stream 1, address 0x123411234
    static localRecycle = RecordType.#addMemory('local-recycle',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Local-recycle memory at stream (?<stream>-?\d+), address (?<address>\w+)/)

    // [MEM]Recycle memory at stream 1, address 0x123411234
    static recycle = RecordType.#addMemory('borrow-recycle',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Borrow-recycle memory at stream (?<stream>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Birth-recycle memory at stream 0, address 0x123411234
    static birthRecycle = RecordType.#addMemory('birth-recycle',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Recycle memory at stream (?<stream>-?\d+), address (?<address>\w+)/)

    // [MEM]Send memory recycling event from stream 0, event id 11, address 0x12341234
    static recycleSendEvent = RecordType.#addMemory('recycle-sent',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Send memory recycling event from stream (?<stream>-?\d+), event id (?<eventId>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Wait memory recycling event at stream 1, event id 11, address 0x12341234
    static recycleWaitEvent = RecordType.#addMemory('recycle-waited',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Wait memory recycling event at stream (?<stream>-?\d+), event id (?<eventId>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Send memory migration event from stream 1, event id 10, address 0x12341234
    static migrationSendEvent = RecordType.#addMemory('migration-sent',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Send memory migration event from stream (?<stream>-?\d+), event id (?<eventId>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Wait memory migration event at stream 0, event id 10, address 0x12341234
    static migrationWaitEvent = RecordType.#addMemory('migration-waited',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]\[MEM]Wait memory migration event at stream (?<stream>-?\d+), event id (?<eventId>-?\d+), address (?<address>0x\w+)/)

    // [MEM]Expand memory pool at stream 1, address 0x123411234, size 65536
    static poolExpand = RecordType.#addPool('pool-expand',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*Expand memory pool at stream (?<stream>((\(nil\))|(0x\w+))), address (?<address>0x\w+), size (?<size>\d+)/,
        (rec, context) => convertRtStreamToStreamId(rec, context, ['stream']))

    // [MEM]Shrink memory pool at stream 1, address 0x12341234
    static poolShrink = RecordType.#addPool('pool-shrink',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*Shrink memory pool at stream (?<stream>((\(nil\))|(0x\w+))), address (?<address>0x\w+)/,
        (rec, context) => convertRtStreamToStreamId(rec, context, ['stream']))


    static sendEvent = RecordType.#addEvent('send-event',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]Sent event (?<eventId>\d+) RT event (?<rtEvent>((\(nil\))|(0x\w+))) from stream (?<stream>-?\d+)/)

    static waitEvent = RecordType.#addEvent('wait-event',
        /.*:(?<time>\d+-\d+-\d+-\d+:\d+:\d+\.\d+\.\d+).*\[KernelTrace]\[(?<name>.*)]Waited event (?<eventId>\d+) RT event (?<rtEvent>((\(nil\))|(0x\w+))) at stream (?<stream>-?\d+)/)

    static #addRecordType(desc, reg, postFunc, category) {
        let rt = new RecordType(reg, desc, postFunc, category);
        RecordType.all[RecordType.all.length] = rt;
        return rt;
    }

    static #addMemory(desc, reg, postFunc) {
        return RecordType.#addRecordType(desc, reg, postFunc, RecordCategory.Memory);
    }

    static #addPool(desc, reg, postFunc) {
        return RecordType.#addRecordType(desc, reg, postFunc, RecordCategory.Pool);
    }

    static #addEvent(desc, reg, postFunc) {
        return RecordType.#addRecordType(desc, reg, postFunc, RecordCategory.Event);
    }

    constructor(reg, desc, postFunc, category) {
        this.reg = reg;
        this.desc = desc;
        this.postFunc = postFunc;
        this.category = category;
    }

    toString() {
        return "RecordType." + this.desc;
    }
}

export function Record(recType, timestamp, rawLog) {
    this.type = recType;
    this.timestamp = timestamp;
    this.log = rawLog;
}

Record.parseFromLog = function (line, context) {
    // pre match to speed up
    if (!(line.includes("[KernelTrace]") || line.includes("memory pool"))) {
        return null;
    }
    for (let rt of RecordType.all) {
        if (rt.reg === undefined) {
            console.warn("Record type ", rt.desc, " does not have a regex to match");
            continue;
        }
        let match = rt.reg.exec(line);
        if (match !== null) {
            if (!('time' in match.groups)) {
                console.warn("Invalid record type ", rt.desc, "a record type at least match timestamp");
                continue
            }
            let rec = new Record(rt, convertLogTimeStringToStamp(match.groups.time), line);
            for (let g of Object.entries(match.groups)) {
                if (g[0] === 'type' || g[0] === 'time') {
                    continue;
                }
                rec[g[0]] = g[1];
            }
            rec.category = rt.category;
            if (rt.postFunc !== undefined) {
                rec = rt.postFunc(rec, context);
            }
            return rec;
        }
    }
    return null;
}
